# -*- coding: utf-8 -*-
# @File: backend => gameplay_view.py
# @CreateTime: 2025/8/28 11:21
# @Author: EvanZhang
# @Info: 游戏逻辑实现

import random
import asyncio
from datetime import datetime, timedelta
from typing import List, Dict, Tuple, Optional
from itertools import combinations

from apps.models.rooms import Room
from apps.models.room_players import RoomPlayer
from apps.models.hands import Hand
from apps.models.hand_players import HandPlayer
from apps.models.actions import Action
from apps.api.websocket_api import room_manager
from apps.schemas.gameplay_schema import WSMessage

# 扑克牌定义
SUITS = ['♠', '♥', '♦', '♣']
RANKS = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
RANK_VALUES = {rank: i for i, rank in enumerate(RANKS)}

class PokerGame:
    """德州扑克游戏引擎"""
    
    def __init__(self, room_id: int):
        self.room_id = room_id
        self.deck = []
        self.board_cards = []
        self.stage = "preflop"  # preflop, flop, turn, river, showdown
        self.pot = 0
        self.current_bet = 0
        self.active_players = []
        self.folded_players = set()
        self.all_in_players = set()
        self.current_player_index = 0
        self.dealer_position = 0
        self.small_blind_position = 0
        self.big_blind_position = 0
        
    def create_deck(self):
        """创建一副牌"""
        self.deck = [f"{rank}{suit}" for suit in SUITS for rank in RANKS]
        random.shuffle(self.deck)
    
    def deal_hole_cards(self, num_players: int) -> Dict[int, List[str]]:
        """发底牌"""
        hole_cards = {}
        for i in range(num_players):
            hole_cards[i] = [self.deck.pop(), self.deck.pop()]
        return hole_cards
    
    def deal_flop(self):
        """发翻牌"""
        self.deck.pop()  # 烧牌
        self.board_cards.extend([self.deck.pop() for _ in range(3)])
        self.stage = "flop"
    
    def deal_turn(self):
        """发转牌"""
        self.deck.pop()  # 烧牌
        self.board_cards.append(self.deck.pop())
        self.stage = "turn"
    
    def deal_river(self):
        """发河牌"""
        self.deck.pop()  # 烧牌
        self.board_cards.append(self.deck.pop())
        self.stage = "river"
    
    def evaluate_hand(self, hole_cards: List[str], board_cards: List[str]) -> Tuple[int, List[int]]:
        """评估牌型，返回(牌型等级, 比较值列表)"""
        all_cards = hole_cards + board_cards
        best_hand = self.get_best_five_cards(all_cards)
        return self.rank_hand(best_hand)
    
    def get_best_five_cards(self, cards: List[str]) -> List[str]:
        """从7张牌中选出最好的5张"""
        best_hand = None
        best_rank = (-1, [])
        
        for combo in combinations(cards, 5):
            rank = self.rank_hand(list(combo))
            if rank > best_rank:
                best_rank = rank
                best_hand = list(combo)
        
        return best_hand
    
    def rank_hand(self, cards: List[str]) -> Tuple[int, List[int]]:
        """评估5张牌的牌型"""
        # 解析牌面和花色
        ranks = []
        suits = []
        for card in cards:
            if card[-1] in SUITS:
                rank = card[:-1]
                suit = card[-1]
            else:
                # 处理可能的格式问题
                rank = card[:-1] if len(card) > 1 else card
                suit = card[-1]
            ranks.append(RANK_VALUES[rank])
            suits.append(suit)
        
        ranks.sort(reverse=True)
        is_flush = len(set(suits)) == 1
        is_straight = self.is_straight(ranks)
        
        # 统计每个点数的出现次数
        rank_counts = {}
        for rank in ranks:
            rank_counts[rank] = rank_counts.get(rank, 0) + 1
        
        counts = sorted(rank_counts.values(), reverse=True)
        unique_ranks = sorted(rank_counts.keys(), key=lambda x: (rank_counts[x], x), reverse=True)
        
        # 判断牌型
        if is_straight and is_flush:
            if ranks == [12, 11, 10, 9, 8]:  # A-K-Q-J-10同花
                return (9, [12])  # 皇家同花顺
            return (8, [max(ranks)])  # 同花顺
        
        if counts == [4, 1]:
            return (7, unique_ranks)  # 四条
        
        if counts == [3, 2]:
            return (6, unique_ranks)  # 葫芦
        
        if is_flush:
            return (5, ranks)  # 同花
        
        if is_straight:
            return (4, [max(ranks)])  # 顺子
        
        if counts == [3, 1, 1]:
            return (3, unique_ranks)  # 三条
        
        if counts == [2, 2, 1]:
            return (2, unique_ranks)  # 两对
        
        if counts == [2, 1, 1, 1]:
            return (1, unique_ranks)  # 一对
        
        return (0, ranks)  # 高牌
    
    def is_straight(self, ranks: List[int]) -> bool:
        """判断是否为顺子"""
        ranks = sorted(set(ranks))
        if len(ranks) != 5:
            return False
        
        # 普通顺子
        if ranks[-1] - ranks[0] == 4:
            return True
        
        # A-2-3-4-5顺子
        if ranks == [0, 1, 2, 3, 12]:
            return True
        
        return False


async def start_game(room_id: int, user_id: int):
    """
    开始游戏
    
    Args:
        room_id: 房间ID
        user_id: 发起用户ID
    
    Returns:
        Tuple[int, Dict]: (状态码, 响应数据)
    """
    try:
        # 检查房间是否存在
        room = await Room.get_or_none(id=room_id)
        if not room:
            return 404, "房间不存在"
        
        # 检查房间状态
        if room.status == "playing":
            return 400, "游戏已在进行中"
        
        # 获取房间内的玩家
        players = await RoomPlayer.filter(
            room_id=room_id, 
            seat_index__isnull=False,
            left_at__isnull=True
        ).prefetch_related('user')
        
        if len(players) < 2:
            return 400, "至少需要2名玩家才能开始游戏"
        
        # 创建新的牌局
        hand = await Hand.create(
            room=room,
            board_cards="",
            pot_size=0
        )
        
        # 更新房间状态
        room.status = "playing"
        await room.save()
        
        # 初始化游戏引擎
        game = PokerGame(room_id)
        game.create_deck()
        
        # 设置盲注位置
        game.dealer_position = 0
        game.small_blind_position = (game.dealer_position + 1) % len(players)
        game.big_blind_position = (game.dealer_position + 2) % len(players)
        
        # 发底牌
        hole_cards = game.deal_hole_cards(len(players))
        
        # 创建手牌玩家记录
        for i, player in enumerate(players):
            await HandPlayer.create(
                hand=hand,
                user=player.user,
                hole_cards=f"{hole_cards[i][0]},{hole_cards[i][1]}",
                contributed_pot=0
            )
        
        # 处理盲注
        sb_player = players[game.small_blind_position]
        bb_player = players[game.big_blind_position]
        
        # 小盲注
        await Action.create(
            hand=hand,
            user=sb_player.user,
            action_type="small_blind",
            amount=room.sb
        )
        
        # 大盲注
        await Action.create(
            hand=hand,
            user=bb_player.user,
            action_type="big_blind",
            amount=room.bb
        )
        
        # 更新奖池
        hand.pot_size = room.sb + room.bb
        await hand.save()
        
        # 广播游戏开始消息
        message = WSMessage(
            type="game_start",
            data={
                "hand_id": hand.id,
                "room_id": room_id,
                "players": [{
                    "user_id": p.user.id,
                    "username": p.user.username,
                    "seat_index": p.seat_index,
                    "chips": p.left_chips
                } for p in players],
                "small_blind": room.sb,
                "big_blind": room.bb,
                "dealer_position": game.dealer_position
            }
        )
        
        # await room_manager.broadcast(**message.model_dump())
        
        return 200, {
            "hand_id": hand.id,
            "message": "游戏开始成功"
        }
        
    except Exception as e:
        return 500, f"服务器错误: {str(e)}"


async def player_action(room_id: int, user_id: int, action_type: str, amount: Optional[int] = None):
    """
    玩家行动
    
    Args:
        room_id: 房间ID
        user_id: 玩家ID
        action_type: 行动类型
        amount: 下注金额
    
    Returns:
        Tuple[int, Dict]: (状态码, 响应数据)
    """
    try:
        # 检查房间和当前牌局
        room = await Room.get_or_none(id=room_id)
        if not room or room.status != "playing":
            return 400, "游戏未在进行中"
        
        # 获取当前牌局
        current_hand = await Hand.filter(room_id=room_id, finished_at__isnull=True).first()
        if not current_hand:
            return 400, "没有进行中的牌局"
        
        # 检查玩家是否在游戏中
        hand_player = await HandPlayer.get_or_none(hand=current_hand, user_id=user_id)
        if not hand_player:
            return 400, "玩家不在当前牌局中"
        
        # 验证行动类型和金额
        valid_actions = ["fold", "call", "raise", "check", "all_in"]
        if action_type not in valid_actions:
            return 400, f"无效的行动类型: {action_type}"
        
        if action_type == "raise" and (amount is None or amount <= 0):
            return 400, "加注时必须指定有效金额"
        
        # 记录玩家行动
        action = await Action.create(
            hand=current_hand,
            user_id=user_id,
            action_type=action_type,
            amount=amount or 0
        )
        
        # 更新奖池
        if action_type in ["call", "raise", "all_in"] and amount:
            current_hand.pot_size += amount
            hand_player.contributed_pot += amount
            await hand_player.save()
            await current_hand.save()
        
        # 广播玩家行动消息
        message = WSMessage(
            type="player_action",
            data={
                "hand_id": current_hand.id,
                "user_id": user_id,
                "action_type": action_type,
                "amount": amount or 0,
                "pot_size": current_hand.pot_size
            }
        )
        
        await room_manager.broadcast(message.dict())
        
        return 200, {
            "success": True,
            "message": f"行动 {action_type} 执行成功"
        }
        
    except Exception as e:
        return 500, f"服务器错误: {str(e)}"


async def get_game_state(room_id: int):
    """
    获取游戏状态
    
    Args:
        room_id: 房间ID
    
    Returns:
        Tuple[int, Dict]: (状态码, 游戏状态数据)
    """
    try:
        # 检查房间
        room = await Room.get_or_none(id=room_id)
        if not room:
            return 404, "房间不存在"
        
        # 获取当前牌局
        current_hand = await Hand.filter(room_id=room_id, finished_at__isnull=True).first()
        if not current_hand:
            return 400, "没有进行中的牌局"
        
        # 获取牌局玩家
        hand_players = await HandPlayer.filter(hand=current_hand).prefetch_related('user')
        
        # 构建玩家状态列表
        players_data = []
        for hp in hand_players:
            # 获取玩家最后一次行动
            last_action = await Action.filter(hand=current_hand, user=hp.user).order_by('-timestamp').first()
            
            players_data.append({
                "user_id": hp.user.id,
                "username": hp.user.username,
                "contributed_pot": hp.contributed_pot,
                "last_action": last_action.action_type if last_action else None,
                "last_action_amount": last_action.amount if last_action else 0
            })
        
        # 解析公共牌
        board_cards = current_hand.board_cards.split(',') if current_hand.board_cards else []
        
        # 根据公共牌数量判断游戏阶段
        stage = "preflop"
        if len(board_cards) == 3:
            stage = "flop"
        elif len(board_cards) == 4:
            stage = "turn"
        elif len(board_cards) == 5:
            stage = "river"
        
        return 200, {
            "hand_id": current_hand.id,
            "stage": stage,
            "board_cards": board_cards,
            "pot_size": current_hand.pot_size,
            "current_bet": 0,  # 需要计算当前下注额
            "to_act_user_id": None,  # 需要计算当前行动玩家
            "action_deadline": None,
            "players": players_data
        }
        
    except Exception as e:
        return 500, f"服务器错误: {str(e)}"


async def advance_game_stage(room_id: int):
    """
    推进游戏阶段（发公共牌）
    
    Args:
        room_id: 房间ID
    
    Returns:
        Tuple[int, Dict]: (状态码, 响应数据)
    """
    try:
        # 获取当前牌局
        current_hand = await Hand.filter(room_id=room_id, finished_at__isnull=True).first()
        if not current_hand:
            return 400, "没有进行中的牌局"
        
        # 解析当前公共牌
        board_cards = current_hand.board_cards.split(',') if current_hand.board_cards else []
        
        # 初始化游戏引擎
        game = PokerGame(room_id)
        game.create_deck()
        
        # 移除已发的牌（这里简化处理，实际应该保存游戏状态）
        for _ in range(len(board_cards)):
            if game.deck:
                game.deck.pop()
        
        new_cards = []
        stage = ""
        
        if len(board_cards) == 0:
            # 发翻牌
            game.deal_flop()
            new_cards = game.board_cards
            stage = "flop"
        elif len(board_cards) == 3:
            # 发转牌
            game.board_cards = board_cards
            game.deal_turn()
            new_cards = game.board_cards
            stage = "turn"
        elif len(board_cards) == 4:
            # 发河牌
            game.board_cards = board_cards
            game.deal_river()
            new_cards = game.board_cards
            stage = "river"
        elif len(board_cards) == 5:
            # 游戏结束，进行结算
            return await end_hand(room_id)
        
        # 更新数据库中的公共牌
        current_hand.board_cards = ','.join(new_cards)
        await current_hand.save()
        
        # 广播游戏阶段更新消息
        message = WSMessage(
            type="game_stage_update",
            data={
                "hand_id": current_hand.id,
                "stage": stage,
                "board_cards": new_cards,
                "pot_size": current_hand.pot_size
            }
        )
        
        await room_manager.broadcast(message.dict())
        
        return 200, {
            "stage": stage,
            "board_cards": new_cards,
            "message": f"游戏进入{stage}阶段"
        }
        
    except Exception as e:
        return 500, f"服务器错误: {str(e)}"


async def end_hand(room_id: int):
    """
    结束当前牌局并进行结算
    
    Args:
        room_id: 房间ID
    
    Returns:
        Tuple[int, Dict]: (状态码, 响应数据)
    """
    try:
        # 获取当前牌局
        current_hand = await Hand.filter(room_id=room_id, finished_at__isnull=True).first()
        if not current_hand:
            return 400, "没有进行中的牌局"
        
        # 获取所有玩家
        hand_players = await HandPlayer.filter(hand=current_hand).prefetch_related('user')
        
        # 获取公共牌
        board_cards = current_hand.board_cards.split(',') if current_hand.board_cards else []
        
        # 评估所有玩家的牌型
        game = PokerGame(room_id)
        player_hands = []
        
        for hp in hand_players:
            # 检查玩家是否弃牌
            fold_action = await Action.filter(
                hand=current_hand, 
                user=hp.user, 
                action_type="fold"
            ).first()
            
            if not fold_action:
                hole_cards = hp.hole_cards.split(',')
                hand_rank = game.evaluate_hand(hole_cards, board_cards)
                player_hands.append({
                    'player': hp,
                    'hand_rank': hand_rank,
                    'hole_cards': hole_cards
                })
        
        # 按牌型排序，找出获胜者
        player_hands.sort(key=lambda x: x['hand_rank'], reverse=True)
        
        # 简单处理：最好牌型的玩家获得所有奖池
        winners = []
        if player_hands:
            best_rank = player_hands[0]['hand_rank']
            for ph in player_hands:
                if ph['hand_rank'] == best_rank:
                    winners.append(ph)
        
        # 分配奖池
        pot_per_winner = current_hand.pot_size // len(winners) if winners else 0
        pot_distribution = {}
        
        for winner in winners:
            winner['player'].win_amount = pot_per_winner
            await winner['player'].save()
            pot_distribution[winner['player'].user.id] = pot_per_winner
            
            # 更新玩家筹码
            room_player = await RoomPlayer.get(
                room_id=room_id, 
                user=winner['player'].user
            )
            room_player.left_chips += pot_per_winner
            await room_player.save()
        
        # 标记牌局结束
        current_hand.finished_at = datetime.now()
        await current_hand.save()
        
        # 更新房间状态
        room = await Room.get(id=room_id)
        room.status = "waiting"
        await room.save()
        
        # 构建获胜者信息
        winners_info = []
        for winner in winners:
            winners_info.append({
                "user_id": winner['player'].user.id,
                "username": winner['player'].user.username,
                "hand_rank": winner['hand_rank'],
                "hole_cards": winner['hole_cards'],
                "win_amount": pot_per_winner
            })
        
        # 广播游戏结束消息
        message = WSMessage(
            type="game_end",
            data={
                "hand_id": current_hand.id,
                "winners": winners_info,
                "pot_distribution": pot_distribution,
                "board_cards": board_cards
            }
        )
        
        await room_manager.broadcast(message.dict())
        
        return 200, {
            "hand_id": current_hand.id,
            "winners": winners_info,
            "pot_distribution": pot_distribution,
            "message": "牌局结束"
        }
        
    except Exception as e:
        return 500, f"服务器错误: {str(e)}"
